﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace GraphDrawer {
	public partial class DiseasesModels : UserControl {
		Root root;
		DataProvider.DataProviderServiceSoapClient cl = new DataProvider.DataProviderServiceSoapClient();

		string modelML;
		string modelDescription;
		ModelEditor editor;

		private static RoutedEventHandler popUpOk;
		private static RoutedEventHandler popUpClosed;

		public static EmptyPopUp PopUp = new EmptyPopUp();
		public static GraphProvider.GraphProviderServiceSoapClient graphProvider = new GraphProvider.GraphProviderServiceSoapClient();

		public DiseasesModels(Root root) {
			InitializeComponent();

			this.root = root;
			PopUp.Visibility = Visibility.Collapsed;
			LayoutRoot.Children.Add(PopUp);
			editor = new ModelEditor();

			cl.GetAvailableDiseasesCompleted += new EventHandler<DataProvider.GetAvailableDiseasesCompletedEventArgs>(cl_GetAvailableDiseasesCompleted);
			cl.GetDiseaseCompleted += new EventHandler<DataProvider.GetDiseaseCompletedEventArgs>(cl_GetDiseaseCompleted);
			graphProvider.CreateDiseaseModelGraphCompleted += new EventHandler<GraphProvider.CreateDiseaseModelGraphCompletedEventArgs>(graphProvider_CreateDiseaseModelGraphCompleted);

			cl.GetAvailableDiseasesAsync();
		}

		void graphProvider_CreateDiseaseModelGraphCompleted(object sender, GraphProvider.CreateDiseaseModelGraphCompletedEventArgs e) {
			GraphProvider.Graph graph = e.Result;

			Dictionary<int, ModelNode> map = new Dictionary<int, ModelNode>();
			foreach (var node in graph.Vertices) {
				double x = rootCanvas.Width / 2;
				double y = rootCanvas.Height / 2;
				string name = string.Empty;
				string color = string.Empty;
				foreach (var item in node.Keys)
					if (item.Key == "stateName")
						name = item.Value;
					else if (item.Key == "nodeColor")
						color = item.Value;
					else if (item.Key == "nodeX" && item.Value != null)
						x = double.Parse(item.Value);
					else if (item.Key == "nodeY" && item.Value != null)
						y = double.Parse(item.Value);
				CreateNode(map, node.Id, x, y, name, color);
			}
			
			foreach (var edge in graph.Edges) {
				double startX = (double)map[edge.Source].GetValue(Canvas.LeftProperty) + map[edge.Source].Width / 2;
				double startY = (double)map[edge.Source].GetValue(Canvas.TopProperty) + map[edge.Source].Height / 2;
				double endX = (double)map[edge.Target].GetValue(Canvas.LeftProperty) + map[edge.Source].Width / 2;
				double endY = (double)map[edge.Target].GetValue(Canvas.TopProperty) + map[edge.Source].Height / 2;
				
				CreateEdge(map[edge.Source], map[edge.Target], startX, startY, endX, endY);
			}
		}

		void CreateNode(Dictionary<int, ModelNode> map, int id, double x, double y, string name, string color) {
			Canvas RectWithTitle = editor.CreateRectangleWithTitle(name, FromKnownColor(color));

			RectWithTitle.SetValue(Canvas.LeftProperty, 2.5);
			RectWithTitle.SetValue(Canvas.TopProperty, 2.5);

			ModelNode element = new ModelNode() {
				Width = RectWithTitle.Width,
				Height = RectWithTitle.Height
			};

			element.border = RectWithTitle.Children[0] as Rectangle;

			element.Children.Add(RectWithTitle);

			element.SetValue(Canvas.LeftProperty, x);
			element.SetValue(Canvas.TopProperty, y);

			map.Add(id, element);
			rootCanvas.Children.Add(element);
		}

		void CreateEdge(ModelNode source, ModelNode target, double startX, double startY, double endX, double endY) {
			if (startX - source.Width / 2 > endX + target.Width / 2) {
				startX -= source.Width / 2;
				endX += target.Width / 2;
				if (startY - source.Height / 2 > endY + target.Height / 2) {
					startY -= source.Height / 2;
					endY += target.Height / 2;
				}
				else if (startY + source.Height / 2 > endY - target.Height / 2) {
					// nothing
				}
				else {
					startY += source.Height / 2;
					endY -= target.Height / 2;
				}
			}
			else if (startX + source.Width / 2 > endX - target.Width / 2) {
				if (startY - source.Height / 2 > endY + target.Height / 2) {
					startY -= source.Height / 2;
					endY += target.Height / 2;
				}
				else if (startY + source.Height / 2 > endY - target.Height / 2) {
					// nothing
				}
				else {
					startY += source.Height / 2;
					endY -= target.Height / 2;
				}
			}
			else {
				startX += source.Width / 2;
				endX -= target.Width / 2;
				if (startY - source.Height / 2 > endY + target.Height / 2) {
					startY -= source.Height / 2;
					endY += target.Height / 2;
				}
				else if (startY + source.Height / 2 > endY - target.Height / 2) {
					// nothing
				}
				else {
					startY += source.Height / 2;
					endY -= target.Height / 2;
				}
			}
			
			var line = new Line {
				X1 = startX,
				Y1 = startY,
				X2 = endX,
				Y2 = endY,
				Stroke = new SolidColorBrush(Colors.Black),
				StrokeThickness = 3
			};

			Polygon lineArrow = new Polygon();
			lineArrow.Points = new PointCollection();
			lineArrow.Fill = new SolidColorBrush(Colors.Black);
			Point l1;
			Point l2;
			
			// Getting Arrow
			RotateTransform rot1 = new RotateTransform();
			rot1.Angle = 45.0;
			rot1.CenterX = endX;
			rot1.CenterY = endY;

			RotateTransform rot2 = new RotateTransform();
			rot2.Angle = -45.0;
			rot2.CenterX = endX;
			rot2.CenterY = endY;

			double px = startX - endX;
			double py = startY - endY;
			double d = Math.Sqrt(px * px + py * py);
			px /= d;
			py /= d;
			l1 = rot1.Transform(new Point(endX + 15 * px, endY + 15 * py));
			l2 = rot2.Transform(new Point(endX + 15 * px, endY + 15 * py));

			//TODO: arrow
			lineArrow.Points.Add(new Point(endX, endY));
			lineArrow.Points.Add(new Point(l1.X, l1.Y));
			lineArrow.Points.Add(new Point(l2.X, l2.Y));

			ModelEdge lineCanvas = new ModelEdge() {
				Height = line.Height,
				Width = line.Width
			};

			lineCanvas.line = line;
			lineCanvas.lineArrow = lineArrow;

			lineCanvas.Children.Add(line);
			lineCanvas.Children.Add(lineArrow);
			
			rootCanvas.Children.Add(lineCanvas);
		}

		public static Color FromKnownColor(string colorName) {
			Line line = (Line)System.Windows.Markup.XamlReader.Load("<Line xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" Fill=\"" + colorName + "\" />");
			return (Color)line.Fill.GetValue(SolidColorBrush.ColorProperty);
		}

		void cl_GetAvailableDiseasesCompleted(object sender, DataProvider.GetAvailableDiseasesCompletedEventArgs e) {
			int prevIndex = -1;
			if (Diseases.Items.Count > 0)
				prevIndex = Diseases.SelectedIndex;
			Diseases.Items.Clear();
			foreach (string item in e.Result)
				Diseases.Items.Add(item);
			if (Diseases.Items.Count > 0) {
				Diseases.SelectedIndex = (prevIndex >= 0 && prevIndex < Diseases.Items.Count)
					? prevIndex : 0;
				rootCanvas.Children.Clear();
				if (Diseases.SelectedItem != null)
					cl.GetDiseaseAsync(Diseases.SelectedItem.ToString());
			}
		}

		void cl_GetDiseaseCompleted(object sender, DataProvider.GetDiseaseCompletedEventArgs e) {
			modelML = e.Result[0];
			modelDescription = e.Result[1];

			graphProvider.CreateDiseaseModelGraphAsync(modelML);

			Description.Text = modelDescription;
			DescriptionEdit.Text = modelDescription;
		}

		private void SaveDescription_Click(object sender, RoutedEventArgs e) {
			modelDescription = DescriptionEdit.Text;
			Description.Text = DescriptionEdit.Text;

			if (Diseases.SelectedItem != null && modelML != null && modelDescription != null)
				cl.UpdateDiseaseAsync(Diseases.SelectedItem.ToString(), modelML, modelDescription);
		}

		private void exit_Click(object sender, RoutedEventArgs e) {
			root.LoadMainMenu();
		}

		private void Diseases_SelectionChanged(object sender, SelectionChangedEventArgs e) {
			rootCanvas.Children.Clear();
			if (Diseases.SelectedItem != null)
				cl.GetDiseaseAsync(Diseases.SelectedItem.ToString());
		}

		private void CreateNewModel_Click(object sender, RoutedEventArgs e) {
			popUpOk = new RoutedEventHandler(PopUp_OK);
			popUpClosed = new RoutedEventHandler(PopUp_Closed);
			DiseasesModels.PopUp.OK += popUpOk;
			DiseasesModels.PopUp.Closed += popUpClosed;
			DiseasesModels.PopUp.AddUserControl(new ModelEditor());
			DiseasesModels.PopUp.Visibility = Visibility.Visible;
		}

		private void EditChosenModel_Click(object sender, RoutedEventArgs e) {

		}

		private void RemoveChosenModel_Click(object sender, RoutedEventArgs e) {
			if (Diseases.SelectedItem != null) {
				cl.RemoveDiseaseAsync(Diseases.SelectedItem.ToString());
				cl.GetAvailableDiseasesAsync();
				Description.Text = string.Empty;
				DescriptionEdit.Text = string.Empty;
			}
		}

		void PopUp_OK(object sender, RoutedEventArgs e) {
			DiseasesModels.PopUp.OK -= popUpOk;
			cl.GetAvailableDiseasesAsync();
		}

		void PopUp_Closed(object sender, RoutedEventArgs e) {
			DiseasesModels.PopUp.OK -= popUpOk;
			cl.GetAvailableDiseasesAsync();
		}
	}
}
